package org.zeuzgroup.tradeskill.wow;

import java.awt.Color;
import java.awt.Image;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.awt.image.PixelGrabber;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Iterator;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;

import org.slf4j.Logger;

public class Fishing {
	// should be set at constuct
	private Point fMenuStart;

	private Point fMenuEnd;

	private Robot robot;

	private Color[] fMenuColors = new Color[6];

	private ArrayList fLine;

	private ArrayList fHook;

	private ArrayList fBait;

	private ArrayList fSplash;

	private ArrayList fRedFeather;

	private ArrayList fBlueFeather;

	private ArrayList baitPoints = null;

	private ArrayList mouseMarker;

	// setting after scan

	private int fishingFailed = 0;

	private int lootGrabbed = 0;

	static final Logger log = org.slf4j.LoggerFactory.getLogger(Fishing.class);

	public Fishing() {
		// setupColors();
		try {
			robot = new java.awt.Robot();
		} catch (Exception e) {
			// TODO: handle exception
		}

		// setupColors(); Currently not working
		// scanforBase(myRobot);
		loadColors();
		fish();

	}

	private void logOff(Robot robot) {
		robot.mouseMove(60, 710);
		robot.mousePress(InputEvent.BUTTON1_MASK);
		robot.delay(500);
		robot.mouseRelease(InputEvent.BUTTON1_MASK);
		robot.delay(500);
		robot.mousePress(InputEvent.BUTTON3_MASK);
		robot.delay(500);
		robot.mouseRelease(InputEvent.BUTTON3_MASK);
		log.info("Loggin off");

	}

	private void keyHitandRelease(Robot robot, int event) {
		robot.keyPress(event);
		robot.delay(500);
		robot.keyRelease(event);
	}

	private void watchSplash(Point baitPoint) {
		robot.mouseMove(700, 0);
		boolean fishing = true;
		long start = Calendar.getInstance().getTimeInMillis();
		Point aRect = new Point(baitPoint.x - 40, baitPoint.y - 40);
		Point bRect = new Point(baitPoint.x + 40, baitPoint.y + 40);
		Color white = new Color(255, 255, 255);
		// fSplash.clear();
		fSplash.add(white);
		while (fishing) {

			boolean splashdetected = false;
			ArrayList splash = scanForColors(fSplash, aRect, bRect, robot);
			Iterator iterSplash = splash.iterator();
			int splashCount = 0;
			while (iterSplash.hasNext()) {
				Point splashPoint = (Point) iterSplash.next();

				log.info("possible splash point detected(" + splashPoint.x
						+ "," + splashPoint.y + ")");
				splashCount++;
				log.info("spash count:" + splashCount);
				if (splashCount > 100) {
					fishing = false;
					log.info("Too much splash count" + splashCount);
					break;
				}

			}

			if (splashCount > 0) {
				log.info("splash count:" + splashCount);
				splashdetected = true;
			}
			if (splashdetected) {
				if (pickupLoot(baitPoint)) {
					fishing = false;
					lootGrabbed++;
				} else {
					fishing = false;
					fishingFailed++;
				}
			}
			if (start + 25000 < Calendar.getInstance().getTimeInMillis()) {
				fishing = false;
			}
			Color fishingProgress = robot.getPixelColor(422, 600);
			if (!(fishingProgress.getRed() < 10
					&& fishingProgress.getGreen() > 130 && fishingProgress
					.getBlue() < 10)) {
				fishing = false;
				fishingFailed++;
				log
						.error("no longer fishing, should never go here unless something is wrong");
			}
			robot.delay(100);
		}
	}

	private boolean pickupLoot(Point grabPoint) {
		// pickup loot

		robot.setAutoDelay(1);
		robot.mouseMove(grabPoint.x, grabPoint.y - 10);
		// robot.keyPress(KeyEvent.SHIFT_DOWN_MASK);
		robot.mousePress(InputEvent.BUTTON3_MASK);
		robot.delay(100);
		robot.mouseRelease(InputEvent.BUTTON3_MASK);

		// susspecting mouse to have moved if there was a bite!!!
		Point currentPos = java.awt.MouseInfo.getPointerInfo().getLocation();

		if (currentPos.distance(grabPoint.x, grabPoint.y - 10) > 10) {
			robot.delay(1000);
			robot.mousePress(InputEvent.BUTTON1_MASK);
			robot.delay(500);
			robot.mouseRelease(InputEvent.BUTTON1_MASK);
			robot.delay(500);
			robot.mousePress(InputEvent.BUTTON1_MASK);
			robot.delay(500);
			robot.mouseRelease(InputEvent.BUTTON1_MASK);
			robot.delay(500);
			robot.mouseMove(700, 0);

			robot.setAutoDelay(0);
			log.info("Picked up loot!");
			return true;
		} else {
			// mouse did not move hence no fish, move mouse back!
			robot.mouseMove(700, 0);
			log.info("no loot!, fake splash!");
			return false;

		}

	}

	private void fish() {
		// call fish macro
		// call splash detection
		int oldInterrupt = 0;
		int interuptedInrow = 0;
		int fishCount = 500;
		Point quit = new Point(0, 0);
		Point currentPos;
		for (int i = 0; i < fishCount; i++) {

			currentPos = java.awt.MouseInfo.getPointerInfo().getLocation();

			if (currentPos.distance(quit) < 100) {
				break;
			}
			keyHitandRelease(robot, KeyEvent.VK_ENTER);
			keyHitandRelease(robot, KeyEvent.VK_ENTER);
			robot.mouseMove(30, 710);
			robot.mousePress(InputEvent.BUTTON1_MASK);
			robot.delay(500);
			robot.mouseRelease(InputEvent.BUTTON1_MASK);
			robot.delay(500);
			robot.mousePress(InputEvent.BUTTON3_MASK);
			robot.delay(500);
			robot.mouseRelease(InputEvent.BUTTON3_MASK);

			// keyHitandRelease(robot, KeyEvent.VK_1);

			Point bait = findBaiteAlternate(robot);
			oldInterrupt = fishingFailed;
			if (bait != null) {
				watchSplash(bait);
			}
			if (fishingFailed == oldInterrupt) {
				log.debug("No interupt");
				interuptedInrow = 0;

			} else {
				log.debug("Interupted");
				interuptedInrow++;
			}
			if (interuptedInrow > 6) {
				log.error("Possible Attack!");
				// check for hp count!
				// i = fishCount;
				// break;
			}
		}
		logOff(robot);
	}

	private void setupColors() {
		String filename = "E:\\develop\\eclipsework\\macrotool\\console\\mockLesserColors.tga";
		Image img = null;
		try {
			// img = TargaFile.getBufferedImage(filename);
			img = this.loadImage(filename);

		} catch (Exception e) {
			log.error(e.getMessage());
		}

		// load respective colors into arrays
		writeTime();
		log.info("scanning line start");
		fLine = handlepixels(img, 0, 0, 200, 100);
		log.info("scanning line end");

		log.info("scanning red feather start");
		fRedFeather = handlepixels(img, 0, 100, 200, 100);
		log.info("scanning red feather end");

		log.info("scanning baitbase start");
		fBait = handlepixels(img, 0, 200, 200, 100);
		log.info("scanning baitbase end");

		log.info("scanning blue feather start");
		fBlueFeather = handlepixels(img, 0, 300, 200, 100);
		log.info("scanning blue feather end");

		log.info("scanning hook start");
		fHook = handlepixels(img, 0, 400, 200, 100);
		log.info("scanning hook end");
		writeTime();
	}

	public Color handlesinglepixel(int x, int y, int pixel) {
		int alpha = (pixel >> 24) & 0xff;
		int red = (pixel >> 16) & 0xff;
		int green = (pixel >> 8) & 0xff;
		int blue = (pixel) & 0xff;

		return new Color(pixel);

	}

	public ArrayList handlepixels(Image img, int x, int y, int w, int h) {
		int[] pixels = new int[w * h];
		Color color = new Color(255, 255, 255);
		PixelGrabber pg = new PixelGrabber(img, x, y, w, h, pixels, 0, w);
		try {
			pg.grabPixels();
		} catch (InterruptedException e) {
			System.err.println("interrupted waiting for pixels!");
			return null;
		}
		if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
			System.err.println("image fetch aborted or errored");
			return null;
		}
		ArrayList colors = new ArrayList();
		for (int j = 0; j < h; j++) {
			for (int i = 0; i < w; i++) {
				Color current = handlesinglepixel(x + i, y + j, pixels[j * w
						+ i]);
				if (!current.equals(color)) {

					Iterator iter = colors.iterator();
					boolean match = false;
					while (iter.hasNext()) {
						Color nextColor = (Color) iter.next();
						if (current.getRed() == nextColor.getRed()
								&& current.getGreen() == nextColor.getGreen()
								&& current.getBlue() == nextColor.getBlue()) {
							match = true;
							break;

						}
					}
					if (!match) {
						colors.add(current);

						System.out.println("color nr " + colors.size()
								+ " found:(" + current.getRed() + ","
								+ current.getGreen() + "," + current.getBlue()
								+ ")(" + j + "," + i + ")");
					}
				}

			}
		}
		return colors;
	}

	public static BufferedImage loadImage(final String filename)
			throws Exception {
		// create a URL from the specified filename
		URL imageUrl = Fishing.class.getClassLoader().getResource(filename);
		imageUrl = new File(filename).toURL();
		if (imageUrl == null) {

			throw new Exception("The filename \"" + filename
					+ "\" could not be found.");
		} /* else -- the filename was found */

		// set no caching for ImageIO as there should be no rewinds
		// NOTE: having caching enabled *severely* slows down image reading
		// performance
		// NOTE: this does not need to be set each time since it's local to
		// each thread group.
		ImageIO.setUseCache(false /* don't use file caching */);

		InputStream inputStream = null;
		ImageInputStream imageStream = null;
		BufferedImage bufferedImage = null;
		try {
			// NOTE: due to a re-emergence of:
			// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6331418
			// ImageIO.read() cannot be used directly. This is
			// effectively a copy of ImageIO.read() that catches the
			// IllegalArgumentException and tries the next available
			// ImageReader.
			// TODO: determine if there is a way (via classpath tricks) to set
			// the order of the service providers so that precedence can
			// be given to the RI provider

			inputStream = imageUrl.openStream();
			imageStream = ImageIO.createImageInputStream(inputStream);
			if (imageStream == null)
				throw new Exception(
						"\""
								+ filename
								+ "\" could not be loaded.  Probably an unknown format.");
			/* else -- there is an ImageInputStream for the image */
			final Iterator<ImageReader> readers = ImageIO
					.getImageReaders(imageStream);
			if (!readers.hasNext())
				throw new Exception(
						"\""
								+ filename
								+ "\" could not be loaded.  Probably an unknown format.");
			/* else -- there are ImageReader's for the image */

			// try ImageReaders until a non-null BufferedImage is loaded
			while (readers.hasNext()) {
				try {
					final ImageReader reader = readers.next();
					reader.setInput(imageStream, true/* seek forward only */,
							true/* ignore metadata */);
					bufferedImage = reader
							.read(0, reader.getDefaultReadParam());
					reader.dispose();

					if (bufferedImage != null)
						break;
					/* else -- the image was not loaded */
				} catch (final IllegalArgumentException iae) {
					// NOTE: this is explicitly caught for bug:
					// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6331418

					// try the next ImageReader
					continue;
				}
			}

			// NOTE: intentionally commented out (see above NOTEs)
			// // read the image from the URL
			// bufferedImage = ImageIO.read(imageUrl);
		} catch (IOException ioe) {
			// the image could not be read or there was an error reading it
			throw new Exception(ioe);
		} finally {
			try {
				if (imageStream != null)
					imageStream.close();
				/* else -- the ImageStream was never created */

				inputStream.close();
			} catch (final IOException ioe) {
				// an error occurred while closing
				// NOTE: an outer IOException may have been thrown in which case
				// this exception would shadow it. It is acceptible in
				// this case since they are the same exception (though
				// the potentially more meaningful reason would be lost).
				throw new Exception(ioe);
			}
		}

		// if the buffered image is null then it could not be loaded
		if (bufferedImage == null)
			throw new Exception("\"" + filename
					+ "\" could not be loaded.  Probably an unknown format.");
		/* else -- the buffered image is loaded */

		// flip the image vertically
		// NOTE: OpenGL specifies that (0, 0) is in the lower-left corner
		// whereas a BufferedImage's (0, 0) is in the *upper*-left corner.
		final AffineTransform transform = AffineTransform.getScaleInstance(1,
				-1);
		transform.translate(0, -bufferedImage.getHeight(null));
		final AffineTransformOp op = new AffineTransformOp(transform,
				AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
		bufferedImage = op.filter(bufferedImage, null);

		return bufferedImage;
	}

	private void loadColors() {
		fRedFeather = loadArray("redfeather.dat");
		fBlueFeather = loadArray("bluefeather.dat");
		fBait = loadArray("bluefeather.dat");
		fHook = loadArray("hook.dat");
		fLine = loadArray("line.dat");
		fSplash = loadArray("splash.dat");
		mouseMarker = loadArray("mouse.dat");
	}

	private void scanforBase(Robot robot) {
		Point redFeatherA = new Point(0, 100);
		Point redFeatherB = new Point(200, 200);

		System.out.println("scanning red feather start");
		fRedFeather = scanColors(redFeatherA, redFeatherB, robot);
		saveArray(fRedFeather, "redfeather.dat");
		System.out.println("scanning red feather end");

		Point blueFeatherA = new Point(0, 300);
		Point blueFeatherB = new Point(200, 400);

		System.out.println("scanning blue feather start");
		fBlueFeather = scanColors(blueFeatherA, blueFeatherB, robot);
		saveArray(fBlueFeather, "bluefeather.dat");
		System.out.println("scanning blue feather end");

		Point baitBaseA = new Point(0, 200);
		Point baitBaseB = new Point(200, 300);

		System.out.println("scanning baitbase start");
		fBait = scanColors(baitBaseA, baitBaseB, robot);
		saveArray(fBait, "bait.dat");
		System.out.println("scanning baitbase end");

		Point hookA = new Point(0, 400);
		Point hookB = new Point(200, 500);
		System.out.println("scanning hook start");
		fHook = scanColors(hookA, hookB, robot);
		saveArray(fHook, "hook.dat");
		System.out.println("scanning hook end");

		Point lineA = new Point(0, 0);
		Point lineB = new Point(200, 100);
		System.out.println("scanning line start");
		fLine = scanColors(lineA, lineB, robot);
		saveArray(fLine, "line.dat");
		System.out.println("scanning line end");

		Point SplashA = new Point(0, 500);
		Point SplashB = new Point(200, 600);
		System.out.println("scanning splash start");
		fSplash = scanColors(SplashA, SplashB, robot);
		saveArray(fSplash, "splash.dat");
		System.out.println("scanning splash end");

		Point mouseA = new Point(0, 600);
		Point mouseB = new Point(200, 700);
		System.out.println("scanning mouse start");
		fSplash = scanColors(mouseA, mouseB, robot);
		saveArray(fSplash, "mouse.dat");
		System.out.println("scanning mouse end");

	}

	private ArrayList loadArray(String filename) {// Read from disk using
		// FileInputStream
		try {

			FileInputStream f_in = new FileInputStream(filename);

			// Read object using ObjectInputStream
			ObjectInputStream obj_in = new ObjectInputStream(f_in);

			// Read an object
			Object obj = obj_in.readObject();

			if (obj instanceof ArrayList) {
				return (ArrayList) obj;
			}
		} catch (Exception e) {
			// TODO: handle exception
		}
		return null;
	}

	private void saveArray(ArrayList myList, String filename) {
		File file = new File(filename);
		try {
			file.createNewFile();
			// Write to disk with FileOutputStream
			FileOutputStream f_out = new FileOutputStream(file);
			// Write object with ObjectOutputStream
			ObjectOutputStream obj_out = new ObjectOutputStream(f_out);

			// Write object out to disk
			obj_out.writeObject(myList);

		} catch (Exception e) {
			// TODO: handle exception
		}

	}

	private void writeTime() {
		Calendar cal = new GregorianCalendar();
		int min = cal.get(Calendar.MINUTE); // 0..59
		int sec = cal.get(Calendar.SECOND); // 0..59
		int ms = cal.get(Calendar.MILLISECOND);
		log.info("time :" + min + ":" + sec + ":" + ms);
	}

	private Point findBaiteAlternate(Robot robot) {
		Point aRect = new Point(400, 200);
		Point bRect = new Point(624, 350);
		boolean baitFound = false;
		Point bait = null;
		Color black = new Color(0, 0, 0);

		for (int x = aRect.x; x < bRect.x; x++) {
			x = x + 10;
			for (int y = aRect.y; y < bRect.y; y++) {
				y = y + 6;
				robot.mouseMove(x, y);
				robot.delay(10);
				Color current = robot.getPixelColor(x + 1, y);

				Color ref2 = robot.getPixelColor(x + 2, y);
				if (current.equals(black) && ref2.equals(black)) {
					Color ref3 = robot.getPixelColor(x + 3, y);
					Color ref4 = robot.getPixelColor(x + 4, y);
					if (ref3.equals(black) && ref4.equals(black)) {
						robot.mouseMove(x + 50, y + 50);
						current = robot.getPixelColor(x - 1, y - 1);
						if (!current.equals(black)) {
							bait = new Point(x, y);
							log.info("bait found (" + x + "," + y + ")");
							robot.mouseMove(x, y);
							return bait;
						}
					}
				}

			}

		}

		return bait;

	}

	private ArrayList scanForColor(Color color, Point aRect, Point bRect,
			Robot robot) {
		writeTime();
		ArrayList points = new ArrayList();
		for (int x = aRect.x; x < bRect.x; x++) {
			for (int y = aRect.y; y < bRect.y; y++) {
				Color current = robot.getPixelColor(x, y);
				// robot.mouseMove(x, y);
				if (match(current, color)) {
					Point point = new Point(x, y);
					points.add(point);
					log.info("target color found at:(" + point.x + ","
							+ point.y + ")");

				}

			}

		}

		writeTime();
		return points;
	}

	private ArrayList scanForColors(ArrayList colors, Point aRect, Point bRect,
			Robot robot) {

		ArrayList points = new ArrayList();
		for (int x = aRect.x; x < bRect.x; x++) {
			for (int y = aRect.y; y < bRect.y; y++) {

				Color current = robot.getPixelColor(x, y);
				for (int i = 0; i < colors.size(); i++) {
					Color color = (Color) colors.get(i);

					if (current.equals(color)) {
						Point point = new Point(x, y);
						points.add(point);
						// System.out.println("target color found at:(" +
						// point.x
						// + "," + point.y + ")");

					}
				}

			}

		}

		return points;
	}

	private ArrayList scanForColor(Color color, Point aRect, Point bRect,
			Robot robot, int offset) {
		writeTime();
		ArrayList points = new ArrayList();
		for (int x = aRect.x; x < bRect.x; x++) {
			for (int y = aRect.y; y < bRect.y; y++) {
				Color current = robot.getPixelColor(x, y);
				robot.mouseMove(x, y);
				if (match(current, color, offset)) {
					Point point = new Point(x, y);
					points.add(point);
					System.out.println("target color found at:(" + point.x
							+ "," + point.y + ")");
					System.out.println("base rgb:" + color.getRed() + ","
							+ color.getGreen() + "," + color.getBlue()
							+ " sample:" + current.getRed() + ","
							+ current.getGreen() + "," + current.getBlue()
							+ " offset:" + offset);

				} else {
					System.out.println("scanned for base rgb:" + color.getRed()
							+ "," + color.getGreen() + "," + color.getBlue()
							+ " but got sample:" + current.getRed() + ","
							+ current.getGreen() + "," + current.getBlue()
							+ " offset:" + offset);

				}

			}

		}

		writeTime();
		return points;
	}

	private ArrayList scanColors(Point aRect, Point bRect, Robot robot) {
		writeTime();
		Color color = new Color(255, 255, 255);
		ArrayList colors = new ArrayList();
		int nrColors = 0;
		for (int x = aRect.x; x < bRect.x; x++) {
			for (int y = aRect.y; y < bRect.y; y++) {
				Color current = robot.getPixelColor(x, y);
				// robot.mouseMove(x, y);
				if (!current.equals(color)) {
					Iterator iter = colors.iterator();
					boolean match = false;
					while (iter.hasNext()) {
						Color nextColor = (Color) iter.next();
						if (current.getRed() == nextColor.getRed()
								&& current.getGreen() == nextColor.getGreen()
								&& current.getBlue() == nextColor.getBlue()) {
							match = true;
							break;

						}
					}
					if (!match) {
						colors.add(current);
						nrColors++;
						log.info("color nr " + nrColors + " found:("
								+ current.getRed() + "," + current.getGreen()
								+ "," + current.getBlue() + ")(" + x + "," + y
								+ ")");
					}
				}

			}

		}

		writeTime();
		return colors;
	}

	private boolean match(Color sample, Color ref) {
		int offsetR = 5;
		int offsetG = 5;
		int offsetB = 5;
		boolean redMatch = false;
		boolean greenMatch = false;
		boolean blueMatch = false;
		if (sample.getRed() < ref.getRed() + offsetR
				&& sample.getRed() > ref.getRed() - offsetR) {
			redMatch = true;
		}
		if (sample.getGreen() < ref.getGreen() + offsetG
				&& sample.getGreen() > ref.getGreen() - offsetG) {
			greenMatch = true;
		}
		if (sample.getBlue() < ref.getBlue() + offsetB
				&& sample.getBlue() > ref.getBlue() - offsetB) {
			blueMatch = true;
		}
		if (redMatch && blueMatch && greenMatch) {
			return true;
		}

		return false;
	}

	private boolean match(Color sample, Color ref, int offset) {
		int offsetR = offset;
		int offsetG = offset;
		int offsetB = offset;
		boolean redMatch = false;
		boolean greenMatch = false;
		boolean blueMatch = false;
		if (sample.getRed() < ref.getRed() + offsetR
				&& sample.getRed() > ref.getRed() - offsetR) {
			redMatch = true;
		}
		if (sample.getGreen() < ref.getGreen() + offsetG
				&& sample.getGreen() > ref.getGreen() - offsetG) {
			greenMatch = true;
		}
		if (sample.getBlue() < ref.getBlue() + offsetB
				&& sample.getBlue() > ref.getBlue() - offsetB) {
			blueMatch = true;
		}
		if (redMatch && blueMatch && greenMatch) {
			return true;
		}

		return false;
	}

}
